package com.monkey.service;

import com.monkey.core.ErrorCode;
import com.monkey.generator.dao.*;
import com.monkey.generator.model.*;
import com.monkey.model.monkey.Monkey;
import com.monkey.model.monkey.MonkeyBonus;
import com.monkey.model.user.User;
import com.monkey.util.CountUtil;
import com.monkey.util.DateUtil;
import com.monkey.util.WebConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.monkey.service.MonkeyService.MONKEY_STATUS_NORMAL;
import static com.monkey.service.UserService.ACTION_BONUS;

/**
 * desc:
 * auth: chenxiaobin
 * data: 2018/1/17.
 */
@Service
@Transactional
public class BonusService {
    private static Logger logger = LoggerFactory.getLogger(BonusService.class);

    @Resource
    private UserService userService = null;

    @Resource
    private MonkeyService monkeyService = null;

    @Resource
    private TUserMapper userMapper = null;

    @Resource
    private TUserActionMapper userActionMapper = null;

    @Resource
    private TUserBonusMapper userBonusMapper = null;

    @Resource
    private TMonkeyMapper monkeyMapper = null;

    @Resource
    private TMonkeyBonusMapper monkeyBonusMapper = null;

    @Resource
    private THistoryBonusMapper historyBonusMapper = null;

    @Resource
    private JdbcTemplate jdbcTemplate = null;

    @Resource
    private CountUtil countUtil = null;

    @Resource
    private DateUtil dateUtil = null;

    @Resource
    private WebConfig webConfig = null;

    public void init() {
    }

    public void countBonus() {
        logger.info("===== bonus start =====");
        do {
            // 计算时间周期
            String bonusTime = webConfig.get("bonus.total.time");
            SimpleDateFormat sdf = dateUtil.getSimpleDateFormat();
            SimpleDateFormat sdf2 = dateUtil.getDateFormat();
            Date now = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            calendar.add(Calendar.HOUR_OF_DAY, -24);
            String str1 = sdf.format(now) + " " + bonusTime;
            String str2 = sdf.format(calendar.getTime()) + " " + bonusTime;
            Date date1, date2;
            try {
                date1 = sdf2.parse(str1);
                date2 = sdf2.parse(str2);
            } catch (Exception e) {
                logger.info("bonus time error from:{} to:{}", str2, str1);
                break;
            }
            logger.info("bonus time from:{} to:{}", str2, str1);

            // 计算当天收入
            float bonusPercent = webConfig.getFloat("bonus.total.percent");
            TUserActionExample example = new TUserActionExample();
            example.or().andCreatetimeGreaterThan(date2).andCreatetimeLessThan(date1);
            List<TUserAction> actions = userActionMapper.selectByExample(example);
            if (null == actions || actions.isEmpty()) {
                logger.info("bonus money empty");
                break;
            }
            float luck = getLuck(now);
            float moneyTotal = 0;
            for (TUserAction action : actions) {
                moneyTotal += action.getWater().floatValue();
            }
            moneyTotal = moneyTotal * bonusPercent; // 按比例分红
            logger.info("bonus money:{}, {}, {}, {}", moneyTotal + luck, luck, moneyTotal, bonusPercent * 100);
            moneyTotal += luck;

            THistoryBonus historyBonus = new THistoryBonus();
            historyBonus.setTotal(new BigDecimal(moneyTotal));
            historyBonus.setCreatetime(sdf2.format(now));
            if (0 == historyBonusMapper.insert(historyBonus)) {
                logger.info("bonus insert total error:{}", historyBonus);
            }

            // 获取所有当天有喂食的宠物快照
            String sql = "SELECT data1 AS monkeyId, sum(water) AS num FROM user_action WHERE action=1 AND createTime>? AND createTime<? GROUP BY data1";
            List<Map<String, Object>> monkeyList = jdbcTemplate.queryForList(sql, date2, date1);
            if (null == monkeyList || monkeyList.isEmpty()) {
                logger.info("bonus monkey empty");
                break;
            }
            logger.info("bonus monkey num:{}", monkeyList.size());

            // 留下喂食宠物，计算掘金总数
            int count = 0;
            int delay = webConfig.getInt("bonus.count.delay");
            int size = webConfig.getInt("bonus.count.size");
            float bonusTotal = 0;
            float goldTotal = 0;
            List<MonkeyData> monkeys = new ArrayList<>();
            for (Map<String, Object> m : monkeyList) {
                if (++count > size) {
                    try {
                        logger.info("bonus money sleep");
                        Thread.sleep(delay);
                    } catch (Exception e) {
                        logger.info("bonus money sleep timeout");
                    }
                    count = 0;
                }
                int monkeyId = (int) m.get("monkeyId");
                int num = ((BigDecimal) m.get("num")).intValue();
                Monkey monkey = monkeyService.getMonkey(monkeyId);
                if (null == monkey) {
                    logger.info("bonus monkey count null:{}", monkeyId);
                    continue;
                }
                monkeys.add(new MonkeyData(monkey, num));

                // 宠物掘金分数=体重*掘金属性值*当日有效投食金额/代系数
                float bonus = monkey.getBonus() * num / countUtil.generation(monkey.getGeneration());
                logger.info("bonus monkey count:{}, {} * {} / {} = {}", monkeyId, monkey.getBonus(),
                        num, countUtil.generation(monkey.getGeneration()), bonus);
                goldTotal += monkey.getBonus() * num;
                bonusTotal += bonus;
            }
            logger.info("bonus total:{}, {}", bonusTotal, goldTotal);

            // 计算每只宠物掘金
            Map<Integer, Float> userBonusMap = new HashMap<>();
            Map<Integer, Float> userGoldMap = new HashMap<>();
            count = 0;
            for (MonkeyData data : monkeys) {
                if (++count > size) {
                    try {
                        logger.info("bonus money sleep");
                        Thread.sleep(delay);
                    } catch (Exception e) {
                        logger.info("bonus money sleep timeout");
                    }
                    count = 0;
                }
                Monkey monkey = data.getMonkey();
                // 宠物掘金分数=体重*掘金属性值*当日有效投食金额/代系数
                float bonus = monkey.getBonus() * data.getCount() / countUtil.generation(monkey.getGeneration());
                float bonusMoney = moneyTotal * bonus / bonusTotal;
                float bonusGold = 1000000 * monkey.getBonus() * data.getCount() / goldTotal;
                logger.info("bonus monkey:{}, {} * {} / {} = {}", monkey.getId(), moneyTotal, bonus, bonusTotal, bonusMoney);
                logger.info("bonus monkey:{}, gold:{}", monkey.getId(), bonusGold);

                if (bonusMoney <= 0.0001f) {
                    logger.info("bonus monkey:{} too small", monkey.getId());
                } else {
                    // 发放奖励
                    BigDecimal bonusMoneyDec = new BigDecimal(bonusMoney);
                    TMonkeyBonus monkeyBonus = new TMonkeyBonus();
                    monkeyBonus.setUserid(monkey.getUserId());
                    monkeyBonus.setMonkeyid(monkey.getId());
                    monkeyBonus.setBonus(bonusMoneyDec);
                    monkeyBonus.setCreatetime(now);
                    if (0 == monkeyBonusMapper.insert(monkeyBonus)) {
                        logger.info("bonus monkey insert bonus error:{}", monkey.getId());
                    }

                    // 更新数据
                    monkey.setBonusCount(monkey.getBonusCount() + 1);
                    monkey.setBonusYesterday(bonusMoneyDec);
                    monkey.setBonusTotal(monkey.getBonusTotal().add(bonusMoneyDec));
                    TMonkey monkey1 = new TMonkey();
                    monkey1.setBonuscount(monkey.getBonusCount());
                    monkey1.setBonusyesterday(bonusMoneyDec);
                    monkey1.setBonustotal(monkey.getBonusTotal());
                    TMonkeyExample example2 = new TMonkeyExample();
                    example2.or().andMonkeyidEqualTo(monkey.getId());
                    example2.setLimit(1);
                    if (0 == monkeyMapper.updateByExampleSelective(monkey1, example2)) {
                        logger.info("bonus monkey update monkey error:{}", monkey.getId());
                    }

                    // 插入记录
                    List<MonkeyBonus> bonuses = monkey.getBonuses();
                    bonuses.add(0, new MonkeyBonus(monkeyBonus, sdf));

                    Float f = userBonusMap.get(monkey.getUserId());
                    if (null == f) {
                        userBonusMap.put(monkey.getUserId(), bonusMoney);
                    } else {
                        userBonusMap.put(monkey.getUserId(), f + bonusMoney);
                    }

                    Float f1 = userGoldMap.get(monkey.getUserId());
                    if (null == f1) {
                        userGoldMap.put(monkey.getUserId(), bonusGold);
                    } else {
                        userGoldMap.put(monkey.getUserId(), f1 + bonusGold);
                    }
                }
            }

            // 记录每个玩家
            for (Map.Entry<Integer, Float> entry : userBonusMap.entrySet()) {
                if (++count > size) {
                    try {
                        logger.info("bonus money sleep");
                        Thread.sleep(delay);
                    } catch (Exception e) {
                        logger.info("bonus money sleep timeout");
                    }
                    count = 0;
                }

                int userId = entry.getKey();
                float bonus = entry.getValue();
                TUserBonus userBonus = new TUserBonus();
                userBonus.setUserid(userId);
                userBonus.setBonus(new BigDecimal(bonus));
                userBonus.setCreatetime(now);
                if (0 == userBonusMapper.insert(userBonus)) {
                    logger.info("bonus money insert bonus error:{}, {}", userId, bonus);
                }

                User user = userService.getUser(userId);
                if (null != user) {
                    if (ErrorCode.SUCCESS != userService.updateUserMoney(user, 0, bonus, 0, MONKEY_STATUS_NORMAL, ACTION_BONUS)) {
                        logger.info("bonus modify money error:{}, {}", userId, bonus);
                    }

                    BigDecimal bonusDes = new BigDecimal(bonus);
                    user.setBonusTotal(user.getBonusTotal().add(bonusDes));

                    TUser user1 = new TUser();
                    user1.setBonustotal(user.getBonusTotal());
                    TUserExample example2 = new TUserExample();
                    example2.or().andUseridEqualTo(userId);
                    example2.setLimit(1);
                    if (0 == userMapper.updateByExampleSelective(user1, example2)) {
                        logger.info("bonus modify bonus total error:{}, {}", userId, bonus);
                    }
                }

                Float gold = userGoldMap.get(userId);
                if (null != gold) {
                    userService.updateUserGold(user, gold, ACTION_BONUS);
                }
            }

            // 清除今天没有掘金但是昨天有掘的宠物信息
            sql = "SELECT mb.monkeyId FROM monkey_bonus AS mb LEFT JOIN (SELECT ua.data1 from user_action AS ua " +
                    "WHERE ua.createTime>? AND ua.createTime<? AND ua.action=1 GROUP BY ua.data1) AS tmp " +
                    "ON mb.monkeyId=tmp.data1 WHERE mb.createTime>? AND mb.createTime<? AND tmp.data1 IS NULL;";
            monkeyList = jdbcTemplate.queryForList(sql, date2, date1, date2, date1);
            if (null == monkeyList || monkeyList.isEmpty()) {
                logger.info("bonus reset monkey empty");
                break;
            }
            logger.info("bonus reset monkey num:{}", monkeyList.size());

            BigDecimal zero = new BigDecimal(0);
            for (Map<String, Object> m : monkeyList) {
                if (++count > size) {
                    try {
                        logger.info("bonus reset money sleep");
                        Thread.sleep(delay);
                    } catch (Exception e) {
                        logger.info("bonus reset money sleep timeout");
                    }
                    count = 0;
                }
                int monkeyId = (int) m.get("monkeyId");
                Monkey monkey = monkeyService.getMonkey(monkeyId);
                if (null == monkey) {
                    logger.info("bonus reset monkey reset null:{}", monkeyId);
                    continue;
                }
                monkey.setBonusYesterday(zero);

                TMonkey monkey1 = new TMonkey();
                monkey1.setBonusyesterday(zero);
                TMonkeyExample example1 = new TMonkeyExample();
                example1.or().andMonkeyidEqualTo(monkey.getId());
                example1.setLimit(1);
                if (0 == monkeyMapper.updateByExampleSelective(monkey1, example1)) {
                    logger.info("bonus monkey reset error:{}", monkey.getId());
                }
                logger.info("bonus monkey reset:{}", monkey.getId());
            }
        } while (false);
        logger.info("===== bonus end =====");
    }

    public float getLuck(Date now) {
        SimpleDateFormat sdf = dateUtil.getSimpleDateFormat();
        String back = webConfig.get("luck.back");
        String[] items = back.split("-");
        Calendar calendar = Calendar.getInstance();
        int size = items.length;
        float total = 0;
        for (int i = 0; i < size; ++i) {
            float item = Integer.valueOf(items[i]) / 100.0f;
            calendar.setTime(now);
            calendar.add(Calendar.HOUR_OF_DAY, -24 * (size - i));
            String dateString = sdf.format(calendar.getTime());
            String sql = "SELECT sum(price) AS total FROM user_luck WHERE createTime=?";
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, dateString);
            if (null == list || list.isEmpty() || null == list.get(0) || null == list.get(0).get("total")) {
                logger.info("bonus luck empty:{}", dateString);
                continue;
            }
            int total1 = ((BigDecimal) list.get(0).get("total")).intValue();
            total = total + total1 * item;
            logger.info("bonus luck:{}, {}, {}, {}", dateString, total1, item, total);
        }
        return total;
    }

    public class MonkeyData {
        private Monkey monkey;
        private int count;

        public MonkeyData(Monkey monkey, int count) {
            this.monkey = monkey;
            this.count = count;
        }

        public Monkey getMonkey() {
            return monkey;
        }

        public int getCount() {
            return count;
        }
    }
}
